package com.zwcl.common.web.handler;

import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.fastjson.JSON;
import com.zwcl.common.core.domain.entity.ApiResult;
import com.zwcl.common.core.enums.ExceptionCode;
import com.zwcl.common.core.exception.BaseException;
import com.zwcl.common.core.exception.BusinessException;
import com.zwcl.common.core.exception.DbOperateException;
import com.zwcl.common.core.utils.StringUtilsEx;
import com.zwcl.common.web.domain.RequestDetail;
import com.zwcl.common.web.utils.RequestDetailContext;
import feign.FeignException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author xyp
 * @Description 统一异常处理器
 * @date 2019/7/26
 * TODO:待添加邮件提醒
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    //@Autowired
    //private WarnService warnService;

    /**
     * 基础异常
     */
    @ExceptionHandler(BaseException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    public ApiResult baseException(BaseException e)  {
        printRequestDetail();
        log.error(e.getMessage(), e);
        return ApiResult.fail(e.getMessage());
    }

    /**
     * 业务异常
     */
    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    public ApiResult businessException(BusinessException e) {
        printRequestDetail();
        if (StringUtilsEx.isNull(e.getErrorCode())){
            return ApiResult.fail(e.getMessage());
        }
        return ApiResult.fail(ExceptionCode.DAO_EXCEPTION, e.getMessage());
    }

    /**
     * 业务异常
     */
    @ExceptionHandler(DbOperateException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    public ApiResult DbOperateException(DbOperateException e) {
        printRequestDetail();
        if (StringUtilsEx.isNull(e.getErrorCode())){
            return ApiResult.fail(e.getMessage());
        }
        return ApiResult.fail(e.getErrorCode(), e.getMessage());
    }

    @ExceptionHandler(FlowException.class)
    @ResponseStatus(HttpStatus.OK)
    public ApiResult flowException(FlowException e) {
        log.error(e.getMessage(), e);
        printRequestDetail();
        return ApiResult.fail(ExceptionCode.REQUEST_DEGRADE_LIMITER.getCode(),e.getMessage());
    }

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.OK)
    public ApiResult handleException(Exception e) {
        log.error(e.getMessage(), e);
        printRequestDetail();
        return ApiResult.fail(e.getMessage());
    }

    /**
     * HTTP method异常处理器
     *
     * @param e
     * @return
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    public ApiResult handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        printRequestDetail();
        log.info("错误的请求方式{}", e.getMessage());
        return ApiResult.fail(ExceptionCode.HTTP_REQUEST_METHOD_NOT_SUPPORTED_EXCEPTION, e.getMessage());
    }

    /**
     * 服务调用异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(FeignException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    public ApiResult handleFeignException(FeignException e) {
        log.error("服务调用异常", e);
        printRequestDetail();
        //warnService.warnFromException(e);
        return ApiResult.fail(ExceptionCode.FEIGN_CALL_EXCEPTION.getCode(), "服务接口不可用，请稍后重试");
    }

    /**
     * 非法参数验证异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    @ResponseStatus(value = HttpStatus.OK)
    public ApiResult<List<String>> handleMethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException ex) {
        printRequestDetail();
        BindingResult bindingResult = ex.getBindingResult();
        List<String> list = new ArrayList<>();
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        for (FieldError fieldError : fieldErrors) {
            list.add(fieldError.getDefaultMessage());
        }
        Collections.sort(list);
        log.error(getApiCodeString(ExceptionCode.PARAMETER_EXCEPTION) + ":" + JSON.toJSONString(list));
        return ApiResult.fail(ExceptionCode.PARAMETER_EXCEPTION, list);
    }

    /**
     * 打印请求详情
     */
    private void printRequestDetail() {
        RequestDetail requestDetail = RequestDetailContext.getRequestDetail();
        if (requestDetail != null) {
            log.error("异常来源：ip: {}, path: {}", requestDetail.getIp(), requestDetail.getPath());
        }
    }

    /**
     * 获取ApiCode格式化字符串
     *
     * @param apiCode
     * @return
     */
    private String getApiCodeString(ExceptionCode apiCode) {
        if (apiCode != null) {
            return String.format("errorCode: %s, errorMessage: %s", apiCode.getCode(), apiCode.getMessage());
        }
        return null;
    }

    /**
     * 打印错误码及异常
     *
     * @param apiCode
     * @param exception
     */
    private void printApiCodeException(ExceptionCode apiCode, Exception exception) {
        log.error(getApiCodeString(apiCode), exception);
    }
}

